<?php

sys::uses("encoder");

class cache
{
    protected $cache_path;
    protected $id_generator;
    protected $file_name_parser;
    protected $file_name_compiler;
    protected $file_generator;
    protected $encodings;

    function __construct($cache_path, $params = array())
    {
        $this->setCachePath($cache_path);

        if (empty($params["IdGenerator"])) {
            $params["IdGenerator"] = array($this, 'generateId');
        }
        $this->setIdGenerator($params["IdGenerator"]);

        if (empty($params["FileNameParser"])) {
            $params["FileNameParser"] = array($this, 'getFileId');
        }
        $this->setFileNameParser($params["FileNameParser"]);

        if (empty($params["FileNameCompiler"])) {
            $params["FileNameCompiler"] = array($this, 'getFileName');
        }
        $this->setFileNameCompiler($params["FileNameCompiler"]);

        if (empty($params["FileGenerator"])) {
            $params["FileGenerator"] = 'file_get_contents';
        }
        $this->setFileGenerator($params["FileGenerator"]);

        if (empty($params["encodings"])) {
            $params["encodings"] = array();
        }
        $this->setEncodings($params["encodings"]);
    }

    protected function setCachePath($value)
    {
        if (! file_exists($value) || ! is_dir($value) || ! is_readable($value)) {
            throw new ArgException();
        }
        $ds = Application::ds();
        $value = str_replace('/', $ds, str_replace('\\', $ds, $value));
        $value = strrpos($value, $ds) !== (strlen($value) - strlen($ds)) ? $value . $ds : $value;
        $this->cache_path = $value;
    }

    protected function setIdGenerator($value)
    {
        if (! is_callable($value)) {
            throw new ArgException();
        }
        $this->id_generator = $value;
    }

    protected function setFileNameParser($value)
    {
        if (! is_callable($value)) {
            throw new ArgException();
        }
        $this->file_name_parser = $value;
    }

    protected function setFileNameCompiler($value)
    {
        if (! is_callable($value)) {
            throw new ArgException();
        }
        $this->file_name_compiler = $value;
    }

    protected function setFileGenerator($value)
    {
        if (! is_callable($value)) {
            throw new ArgException();
        }
        $this->file_generator = $value;
    }

    protected function setEncodings($value)
    {
        $this->encodings = $value;
    }

    protected function generateId($file)
    {
        return md5(filemtime($file) . filesize($file));
    }

    protected function getFileName($file, $id, $encoding = null)
    {
        $pathinfo = pathinfo($file);
        $extension = $pathinfo['extension'] ? "." . $pathinfo['extension'] : "";
        $file_name = $pathinfo['basename'];
        if (strlen($extension)) {
            $file_name = substr($file_name, 0, strlen($file_name) - strlen($extension));
        }

        return $file_name . "." . $id . ($encoding ? "." . $encoding : "") . $extension;
    }

    protected function getFileId($cached_file, $file)
    {
        $pathinfo = pathinfo($file);
        $extension = $pathinfo['extension'] ? "." . $pathinfo['extension'] : "";
        $file_name = $pathinfo['basename'];
        if (strlen($extension)) {
            $file_name = substr($file_name, 0, strlen($file_name) - strlen($extension));
        }

        $escaped_ext = preg_replace('/([.\[\]*{}()+?])/s', "\\\\\\1", $extension);
        $escaped_name = preg_replace('/([.\[\]*{}()+?])/s', "\\\\\\1", $file_name);
        if (preg_match('/^'.$escaped_name.'\.([^.]+)((\.[^.]+)?)'.$escaped_ext.'$/', $cached_file, $matches)) {
            return array(
                'id' => $matches[1],
                'mode' => $matches[2],
            );
        }
        return null;
    }

    protected function getCachedId($file)
    {
        foreach (scandir($this->cache_path) as $cached_file) {
            if (! is_null($id = call_user_func_array($this->file_name_parser, array($cached_file, $file)))) {
                return $id['id'];
            }
        }
        return null;
    }

    protected function clearCache($file)
    {
        foreach (scandir($this->cache_path) as $cached_file) {
            if (! is_null($id = call_user_func_array($this->file_name_parser, array($cached_file, $file)))) {
                unlink($this->cache_path . $cached_file);
            }
        }
    }

    function get($file, $encoding = null)
    {
        if (! file_exists($file) || is_dir($file) || ! is_readable($file)) {
            throw new ArgException();
        }
        $file_id = call_user_func($this->id_generator, $file);
        if ($curr_id = $this->getCachedId($file)) {
            if ($curr_id != $file_id) {
                $this->clearCache($file);
                $this->generateFile($file, $file_id);
            }
        } else {
            $this->generateFile($file, $file_id);
        }

        return $this->cache_path.call_user_func_array($this->file_name_compiler, array($file, $file_id, $encoding));
    }

    function generateFile($file, $file_id)
    {
        $pathinfo = pathinfo($file);
        $extension = $pathinfo['extension'] ? "." . $pathinfo['extension'] : "";
        $file_name = $pathinfo['basename'];
        if (strlen($extension)) {
            $file_name = substr($file_name, 0, strlen($file_name) - strlen($extension));
        }

        $file_content = call_user_func($this->file_generator, $file);

        $cache_file = call_user_func_array($this->file_name_compiler, array($file, $file_id));
        file_put_contents($this->cache_path.$cache_file, $file_content);

        foreach ($this->encodings as $encoding) {
            $cache_file = call_user_func_array($this->file_name_compiler, array($file, $file_id, $encoding));
            try {
                $encoded_string = encoder::encode($file_content, $encoding);
                file_put_contents($this->cache_path.$cache_file, $encoded_string);
            } catch (Exception $e) {}
        }
    }
}

?>